home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 301-325 / disk_319 / cnewssrc / cnews.src.lzh / relay / procart.c < prev    next >
C/C++ Source or Header  |  1989-07-31  |  14KB  |  451 lines

  1. /*
  2.  * process a single incoming article
  3.  */
  4.  
  5. #ifdef DEBUG
  6. #  define STAT(x)    {    fprintf(stderr,"%s: %d, ",__FUNC__,__LINE__);\
  7.                         fprintf x;\
  8.                         fflush(stderr); }
  9. #else
  10. #  define STAT(x)
  11. #endif
  12.  
  13. #include <stdio.h>
  14. #ifndef AMIGA
  15. #  include <sys/types.h>
  16. #endif /* AMIGA */
  17. #include "libc.h"
  18. #include "news.h"
  19. #include "active.h"
  20. #include "control.h"
  21. #include "headers.h"
  22. #include "article.h"
  23. #include "history.h"
  24. #include "io.h"
  25. #include "msgs.h"
  26. #include "system.h"
  27. #include "transmit.h"
  28.  
  29. /*
  30.  * COPYSIZE is the length of a bulk-copying buffer: the bigger the better,
  31.  * though fewer than 3% of articles exceed 8192 bytes (may 1988).
  32.  * It holds header lines first, and later holds bytes of the body.
  33.  * This buffer is allocated once at the start and never deallocated.
  34.  */
  35. #ifndef COPYSIZE
  36. #  ifdef SMALLMEM
  37. #    define COPYSIZE BUFSIZ        /* conserve memory at expense of speed */
  38. #  else
  39. #    define COPYSIZE 8192        /* big enough for worst-case 4.2bsd blks */
  40. #  endif                /* SMALLMEM */
  41. #endif                /* COPYSIZE */
  42.  
  43. extern char *exclude;        /* for erik */
  44. extern boolean okrefusal;    /* flag from command line */
  45.  
  46. /* forwards */
  47. extern void tossorfile(), surveydamage(), reject(), prefuse(), uninsart();
  48. extern char *hdrcopy();
  49. FORWARD void copyart(), cpybody(), insart();
  50. FORWARD statust snuffmayreturn();
  51.  
  52. /*
  53.  * Copy the article on "in" to a temporary name in the news spool directory,
  54.  * unlink temp name; *or* copy into the final names, if known early enough.
  55.  * (Sets a_tmpf in or near hdrmunge() or hdrdump().)
  56.  * If the spool file opened, install the article it contains.
  57.  */
  58.  
  59. statust cpinsart(in, inname, maxima, blvmax)
  60. FILE *in;
  61. register char *inname;
  62. long maxima;
  63. boolean blvmax;                /* believe maxima? */
  64. {
  65.     register struct article *artp;
  66.     register statust status;
  67.     struct article art;
  68.  
  69.     artp = &art;
  70.     artinit(artp);
  71.     artp->a_blvmax = blvmax;
  72.     artp->a_unread = maxima;
  73.  
  74.     /*
  75.      * copyart() may reject() the article, and may fill the disk.
  76.      * it calls fileart and logs rejected articles.
  77.      */
  78.     copyart(artp, in, inname);
  79.  
  80.     if (artp->a_status&ST_REFUSED) {
  81.         /* no good ngs (in fileart) or reject()ed; not serious */
  82.         warning("rejected article");
  83.         artp->a_status &= ~ST_REFUSED;
  84.         /* paranoia; shouldn't happen */
  85.         nnfclose(artp, &artp->a_artf, inname);
  86.     } else if (artp->a_artf == NULL) {
  87.         warning("can't open spool file `%s'", artp->a_tmpf);
  88.         artp->a_status |= ST_DROPPED;
  89.     } else {
  90.         nnfclose(artp, &artp->a_artf, inname);
  91.         insart(artp);    /* logs accepted art.s during transmission */
  92.         if (artp->a_status&ST_JUNKED) {    /* yer welcome, henry */
  93.             artp->a_status &= ~ST_JUNKED;
  94.             timestamp(stdout, (time_t *)NULL);
  95.             (void) printf(" %s j %s junked due to groups `%s'\n",
  96.                 sendersite(nullify(artp->h.h_path)),
  97.                 artp->h.h_msgid, artp->h.h_ngs);
  98.         }
  99.     }
  100.     status = artp->a_status;
  101.     artfree(artp);
  102.     return status;
  103. }
  104.  
  105. /*
  106.  * Copy the next charcnt bytes of "in" (may be not a disk file)
  107.  * to a permanent file under a (possibly) temporary name.
  108.  * After the headers are seen, accept or reject the article.
  109.  * If rejected and the headers fit in core, no files will be opened.
  110.  * Must munge certain headers on the way & remember certain values.
  111.  * hdrmunge() or hdrdump() sets art->a_tmpf & art->a_artf.
  112.  * Unlink art->a_tmpf, if a temporary link.
  113.  */
  114.  
  115. /* ARGSUSED inname */
  116. STATIC void copyart(art, in, inname)
  117. register struct article *art;
  118. register FILE *in;
  119. char *inname;
  120. {
  121.     boolean installed = YES;
  122.     char *body;
  123.  
  124.     body = hdrcopy(art, in);
  125.     hdrdeflt(&art->h);
  126.     STAT((stderr, "calling tossorfile()\n"));
  127.     tossorfile(art, &installed);
  128.     /* assertion: header values (art->h) can be forgotten here */
  129.     STAT((stderr, "calling cpybody()\n"));
  130.     cpybody(art, in, body);
  131.     STAT((stderr, "calling surveydamage()\n"));
  132.     surveydamage(art, &installed);
  133. }
  134.  
  135. /*
  136.  * The loop copies header lines from input to output or a
  137.  * header output cache.  On exit, hdr will contain the first
  138.  * non-header line, if any, left over from the end of header copying.
  139.  *
  140.  * Some people think the loop is ugly; I'm not sure why.
  141.  * If the byte count is positive, read a line; if it doesn't return
  142.  * EOF and is a header, then adjust byte count, stash and munge headers.
  143.  * strlen(line) must be computed before hdrstash is called,
  144.  * as hdrstash (and thus hdrdigest) removes newlines.
  145.  */
  146.  
  147. char *hdrcopy(art, in)                /* first body line, from gethdr */
  148. register struct article *art;
  149. FILE *in;
  150. {
  151.     register char *hdr = NULL;
  152.     long limit;
  153.     int is_hdr = NO;
  154.  
  155.     hdrwretch();                /* reset the header parser */
  156.     limit = (art->a_blvmax? art->a_unread+1: art->a_unread); /* 1 for NUL */
  157.  
  158.     /* 1 is again for NUL */
  159.     while (limit > 1 &&
  160.             (hdr = gethdr(in, &limit, &is_hdr)) != NULL && is_hdr) {
  161.         hdrdigest(art, hdr, strlen(hdr));
  162.         hdr = NULL;            /* freed inside gethdr */
  163.     }
  164.     /* If we read a body line, gethdr has adjusted limit appropriately. */
  165.     art->a_unread = limit - 1;        /* limit updated by gethdr */
  166.     if (is_hdr)                /* no body: header fills limit */
  167.         hdr = NULL;
  168.     return hdr;
  169. }
  170.  
  171. /*
  172.  * Either reject the article described by art, or accept it and file it.
  173.  * If rejecting it, remove any links and give back assigned #'s
  174.  * (art->a_artf may still be open; arguably uninsart should close it).
  175.  * If accepting it, dump any saved headers and file the article.
  176.  * Unlink art->a_tmpf if it's a temporary link.
  177.  */
  178.  
  179. void tossorfile(art, installedp)
  180. register struct article *art;
  181. boolean *installedp;
  182. {
  183.     STAT((stderr, "calling reject()\n"));
  184.     reject(art);                /* duplicate, etc.? */
  185.     if (art->a_status&(ST_DROPPED|ST_REFUSED)) {
  186.         STAT((stderr, "calling uninsart()\n"));
  187.         uninsart(art);
  188.         *installedp = NO;
  189.     } else {
  190.         STAT((stderr, "not rejected; calling hdrdump()\n"));
  191.         hdrdump(art, ALLHDRS);        /* ALLHDRS triggers fileart */
  192.     }
  193.     STAT((stderr, "checking links\n"));
  194.     if (art->a_unlink) {
  195.         /* a_tmpf has had links made to it, so it can be removed. */
  196.         if (unlink(art->a_tmpf) < 0) {
  197.             warning("copyart can't unlink `%s'", art->a_tmpf);
  198.             art->a_status |= ST_ACCESS;
  199.         }
  200.         art->a_unlink = NO;        /* caution */
  201.     }
  202. }
  203.  
  204. /*
  205.  * Copy article body.
  206.  *
  207.  * body will contain the first non-header line, if any, left over from the
  208.  * end of header copying.  Write it.
  209.  *
  210.  * Copy at most COPYSIZE bytes of body at a time and exactly art->a_unread
  211.  * bytes in total, barring EOF or a full disk. Then "block" is no longer
  212.  * needed.  Force the article to disk, mostly for the benefit of control
  213.  * message processing.
  214.  *
  215.  * The copying buffer, block, is static because it is used repeatedly
  216.  * and persists through most of execution, so dynamic allocation and
  217.  * deallocation seems wasteful, but also for the benefit of compilers
  218.  * for odd machines (e.g. PE, 370s) which make implementing "large"
  219.  * automatic arrays difficult.
  220.  */
  221.  
  222. STATIC void cpybody(art, in, body)
  223. register struct article *art;
  224. FILE *in;
  225. register char *body;
  226. {
  227.     register int readcnt;
  228.     static char block[COPYSIZE];
  229.  
  230.     if (body != NULL) {            /* read too far? */
  231.         register int bodylen = strlen(body);
  232.  
  233.         STAT((stderr, "writing first line of body\n"));
  234.         if (art->a_artf && fwrite(body, 1, bodylen, art->a_artf) != bodylen)
  235.             fulldisk(art, spoolnm(art));
  236.         art->a_charswritten += bodylen;
  237.     }
  238.     STAT((stderr, "read blocks\n"));
  239. #ifdef FJE
  240.     while (art->a_unread > 0 && !(art->a_status & ST_DISKFULL)) {
  241.         Chk_Abort(0L);
  242.         readcnt=fread(block,1, (int) min(art->a_unread, COPYSIZE),in);
  243.         STAT((stderr, "read %d bytes\n", readcnt));
  244.         if (readcnt < 1)
  245.             break;
  246.         STAT((stderr, "attempting to write block\n"));
  247.         if (art->a_artf && fwrite(block, 1, readcnt, art->a_artf) != readcnt)
  248.             fulldisk(art, spoolnm(art));
  249.         art->a_unread -= readcnt;
  250.         art->a_charswritten += readcnt;
  251.     }
  252. #else
  253.     for (; art->a_unread > 0 && !(art->a_status&ST_DISKFULL) &&
  254.         (readcnt=fread(block, 1, (int)min(art->a_unread, COPYSIZE), in)) > 0;
  255.             art->a_unread -= readcnt, art->a_charswritten += readcnt) {
  256.         if (art->a_artf != NULL &&
  257.                     fwrite(block, 1, readcnt, art->a_artf) != readcnt)
  258.             fulldisk(art, spoolnm(art));
  259.         fputc('.', stderr);
  260.     }
  261.     fputc('\n', stderr);
  262. #endif /* FJE */
  263.     STAT((stderr, "about to fflush()\n"));
  264.     if (art->a_artf != NULL && fflush(art->a_artf) == EOF)
  265.         fulldisk(art, spoolnm(art));
  266.     STAT((stderr, "returning to caller\n"));
  267. }
  268.  
  269. /*
  270.  * If not yet uninstalled, and the disk filled, uninstall this article
  271.  * to remove any zero-length links and decrement the active article number.
  272.  * The ST_DISKFULL status will prevent a history entry from being generated.
  273.  */
  274.  
  275. void surveydamage(art, installedp)
  276. register struct article *art;
  277. register boolean *installedp;
  278. {
  279.     if (art->a_unread > 0 && art->a_blvmax) {
  280.         (void) fprintf(stderr, "%s: article %s short by %ld bytes\n",
  281.             progname, (art->h.h_msgid != NULL? art->h.h_msgid: ""),
  282.             (long)art->a_unread);
  283.         art->a_status |= ST_SHORT;    /* NB.: don't uninstall this art. */
  284.     }
  285.     if (*installedp && art->a_status&ST_DISKFULL) {
  286.         uninsart(art);
  287.         *installedp = NO;
  288.     }
  289. #ifdef WATCHCORE
  290.     {
  291.         char stbot;
  292.         extern char *sbrk();
  293.  
  294.         printf("debugging memory use: top of data=%u", (unsigned)sbrk(0));
  295.         printf(", bottom of stack=%u\n", (unsigned)&stbot);
  296.     }
  297. #endif
  298. }
  299.  
  300. /*
  301.  * Install the article on art->a_tmpf or art->a_files:
  302.  * The article should have been accepted and filed in copyart().
  303.  * Add history entries for the article.  Log arrival.
  304.  * Transmit the article to our neighbours.
  305.  * Process control mess(age)es.  ctlmsg can call transmit(fakeart,x)
  306.  * and generate log lines for cancels and ihave/sendme.
  307.  */
  308.  
  309. STATIC void insart(art)
  310. register struct article *art;
  311. {
  312.     if (!(art->a_status&(ST_DROPPED|ST_REFUSED|ST_DISKFULL))) {
  313.         if (!art->a_filed)            /* paranoia */
  314.             (void) fprintf(stderr, "%s: %s not filed by copyart!\n",
  315.                 progname, art->h.h_msgid);
  316.         history(art, STARTLOG);
  317.         transmit(art, exclude);        /* writes systems on stdout */
  318.         (void) putchar('\n');        /* ends the log line */
  319.         if (art->h.h_ctlcmd != NULL)
  320.             ctlmsg(art);
  321. #ifdef notdef                    /* it's only a log file! */
  322.         (void) fflush(stdout);        /* crash-proofness */
  323. #endif
  324.     }
  325.     art->a_status &= ~ST_REFUSED;    /* refusal is quite casual & common */
  326. }
  327.  
  328. /*
  329.  * Reject articles.  This can be arbitrarily picky.
  330.  * Only the headers are used to decide, so this can be called before
  331.  * the article is filed.
  332.  * Be sure to put the fastest tests first, especially if they often result
  333.  * in rejections.
  334.  */
  335.  
  336. void reject(art)
  337. register struct article *art;
  338. {
  339.     if (art->h.h_path == NULL) {
  340.         prefuse(art);
  341.         (void) printf("no Path: header\n");
  342.     } else if (alreadyseen(art->h.h_msgid)) {
  343.         STAT((stderr, "called alreadyseen()\n"));
  344.         prefuse(art);
  345.         (void) printf("duplicate\n");
  346.     } else if (art->h.h_path != NULL && hopcount(art->h.h_path) > 0 &&
  347.                 !ngmatch(oursys()->sy_ngs, art->h.h_ngs)) {
  348.         extern boolean histreject;
  349.  
  350.         STAT((stderr, "called alreadyseen(), hopcount(), & ngmatch()\n"));
  351.         /*
  352.          * non-local article, with all bad groups.
  353.          * (local articles with bad groups will be bounced
  354.          * by fileart when the groups aren't in active.)
  355.          */
  356.         if (histreject)
  357.             history(art, NOLOG);
  358.         prefuse(art);
  359.         (void) printf("no subscribed groups in `%s'\n", art->h.h_ngs);
  360.     } else if (art->h.h_approved == NULL && moderated(art->h.h_ngs)) {
  361.         STAT((stderr, "moderated and no approval\n"));
  362.         prefuse(art);
  363.         (void) printf("unapproved article in moderated group(s) `%s'\n",
  364.             art->h.h_ngs);
  365.     } else
  366.         return;            /* art was accepted */
  367.     art->a_status |= ST_REFUSED;
  368.     if (!okrefusal)
  369.         art->a_status |= ST_DROPPED;
  370. }
  371.  
  372. /*
  373.  * print the leader of a refusal message about the article in "art".
  374.  */
  375.  
  376. void prefuse(art)
  377. register struct article *art;
  378. {
  379.     timestamp(stdout, (time_t *)NULL);
  380.     (void) printf(" %s - %s ", sendersite(nullify(art->h.h_path)),
  381.         art->h.h_msgid);
  382. }
  383.  
  384. /*
  385.  * "Uninstall" an article: remove art->a_files (permanent names) and
  386.  * a_tmpf (temporary name if a_unlink set), and return assigned article #'s.
  387.  * If a_unlink isn't set, a_tmpf is a copy of the first link in art->a_files.
  388.  * Must be called before history() is called, else there will be a
  389.  * history entry for the article, but no spool files.
  390.  */
  391.  
  392. void uninsart(art)
  393. register struct article *art;
  394. {
  395.     if (art->a_unlink && art->a_tmpf != NULL) {
  396.         (void) unlink(art->a_tmpf);    /* I don't wanna know... */
  397.         art->a_unlink = NO;
  398.     }
  399.     /* return article numbers (YES) & ignore unlink errors */
  400.     (void) snuffmayreturn(art->a_files, YES);
  401. }
  402.  
  403. statust snufffiles(filelist)        /* just unlink all files in filelist */
  404. char *filelist;
  405. {
  406.     /* don't return article numbers (NO) & return unlink errors */
  407.     return snuffmayreturn(filelist, NO);
  408. }
  409.  
  410. /*
  411.  * Unlink all files in filelist, and optionally return article numbers.
  412.  * When removing a link, note any failure, but don't issue an error message.
  413.  * For one thing, cancel controls fail routinely because the article has been
  414.  * removed manually or never existed (a previous cancel arrived before its
  415.  * subject and generated a fake history entry).
  416.  */
  417.  
  418. STATIC statust snuffmayreturn(filelist, artret)
  419. char *filelist;
  420. boolean artret;        /* return article numbers & note unlink errors? */
  421. {
  422.     register statust status = ST_OKAY;
  423.     register char *arts, *spacep, *slashp, *artnm;
  424.  
  425.     /* this is a deadly tedious job and I really should automate it */
  426.     for (arts = filelist; arts != NULL && arts[0] != '\0';
  427.          arts = (spacep == NULL? NULL: spacep+1)) {
  428.         spacep = index(arts, ' ');
  429.         if (spacep != NULL)
  430.             spacep[0] = '\0';    /* will be restored below */
  431.         artnm = strsave(arts);
  432.         if (spacep != NULL)
  433.             spacep[0] = ' ';    /* restore space */
  434.  
  435.         slashp = index(artnm, FNDELIM);
  436.         if (slashp != NULL)
  437.             slashp[0] = '\0';    /* will be restored below */
  438.         if (artret)
  439.             /* prevartnum will complain on i/o error to active */
  440.             (void) prevartnum(artnm); /* return assigned # */
  441.         if (slashp != NULL)
  442.             slashp[0] = FNDELIM;    /* restore slash */
  443.  
  444.         mkfilenm(artnm);
  445.         if (unlink(artnm) < 0)
  446.             status |= ST_ACCESS;
  447.         free(artnm);
  448.     }
  449.     return status;
  450. }
  451.